<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>绘制带宽度的曲线-小试牛刀1</title>
    <style>
        canvas{
            display: block;
            border: 1px solid #ccc;
        }
    </style>
</head>
<body>
    <canvas width="512" height="512"></canvas>

    <script type="module">
        import { Renderer, Camera, Transform, Geometry, Program, Mesh } from 'https://unpkg.com/ogl';
        import { Vec2 } from "/common/lib/math/Vec2.js"
        // 旋转向量
        Vec2.prototype.rotate = function(radius){
            const c = Math.cos(radius);
            const s = Math.sin(radius);
            const [x,y] = this;
            
            this.x = x * c - y * s;
            this.y = x * s + y * c;
            return this;
        }
        /**
         * 挤压
         * points：曲线的顶点集合
         * thickness：线宽
         * */ 
        function extrude(gl, points, {thickness = 10} = {}){
            const lineWidth = 0.5 * thickness;
            const insidePoints = [];
            const outsidePoints = [];
            let segments = 2;

            // 遍历所有顶点
            for (let i = 1; i < points.length - 1; i++) {
                const point = points[i];
                // 1.处理挤压长度
                // 处理线段的连接处的两个向量
                let v1 = (new Vec2()).sub(point, points[i - 1]).normalize();
                let v2 = (new Vec2()).sub(point, points[i + 1]).normalize();
                // 计算法线方向
                let v1normal = new Vec2(-v1.y, v1.x);
                let v2normal = new Vec2(v2.y, -v2.x);
                
                // 得到挤压方向
                let v = (new Vec2()).add(v1, v2).normalize();
                // 根据挤压角度的余弦值计算挤压长度
                let cos = v.dot(v1normal);
                let len = lineWidth / cos;

                // // 计算法线向量的差值
                // let normalSub = (new Vec2()).sub(v2normal, v1normal);
                // 计算两个法向量的夹角大小
                let radius = Math.acos(v1normal.dot(v2normal));
                console.log(radius)
                
                // 2.处理顶点
                // 处理折线的起始端点
                if(i == 1){
                    const v0 = (new Vec2(...v1normal)).scale(lineWidth);
                    outsidePoints.push((new Vec2()).add(points[0], v0));
                    insidePoints.push((new Vec2()).sub(points[0], v0));
                }

                // 处理折线的中间连接点
                v.scale(len);
                insidePoints.push((new Vec2()).sub(point, v));
                // 将圆弧分成segments片段
                // 初始的向量为当前点的法向量
                for (let j = 0; j < segments; j++) {
                    // // 计算分片的向量
                    // let n = (new Vec2(...normalSub)).scale(j / segments);
                    // // 得到分片之后的新的向量
                    // let c = (new Vec2(...v1normal)).add(n).normalize().scale(lineWidth);
                    // // 根据向量计算新的坐标点
                    // outsidePoints.push((new Vec2()).add(point, c));
                    // 计算分片的夹角
                    let r = radius * j / segments;
                    // 得到分片之后的新的向量
                    let c = (new Vec2(...v1normal)).scale(lineWidth).rotate(r);
                    // 根据向量计算新的坐标点
                    outsidePoints.push((new Vec2()).add(point, c));
                }
                outsidePoints.push((new Vec2()).add(point, v2normal.scale(lineWidth)));

                // 处理折线的结束端点
                if(i == points.length - 2){
                    let n = new Vec2(v2.y, -v2.x);
                    const last = n.scale(lineWidth);
                    outsidePoints.push((new Vec2()).add(points[points.length - 1], last));
                    insidePoints.push((new Vec2()).sub(points[points.length - 1], last));
                }
            }
            // 顶点的数量
            const count = (insidePoints.length + (outsidePoints.length - 2) * (segments + 1) * 2 + 2) * 2;
            const indexCount = (outsidePoints.length - 2) * segments * 3 + (insidePoints.length - 1) * 6;
            const position = new Float32Array(count);
            const index = new Uint16Array(indexCount);
            let indexOffset = 0, posOffset = 0;
            for (let i = 1; i < insidePoints.length - 1; i++) {
                // 除了首尾，其余顶点都被存储两次
                const inside = insidePoints[i];
                const insidePre = insidePoints[i - 1];
                const outsidePre = outsidePoints[(segments + 1) * i - (segments + 1)];
                const outside = outsidePoints[(segments + 1) * i - segments];

                let offset = posOffset;
                index.set([offset, offset + 1, offset + 2, offset, offset + 2, offset + 3], indexOffset);
                position.set([...insidePre, ...outsidePre, ...outside, ...inside], posOffset * 2);

                indexOffset += 6;
                posOffset += 4;
                
                let tmp = posOffset - 1;
                for (let j = 0; j < segments; j++) {
                    // 计算拐角的顶点
                    let k = (segments + 1) * (i - 1) + 1 + j;
                    const outside = outsidePoints[k];
                    const outsideNext = outsidePoints[k + 1];

                    index.set([tmp, posOffset, posOffset + 1], indexOffset);
                    position.set([...outside, ...outsideNext], posOffset * 2);
                    indexOffset += 3;
                    posOffset += 2;
                }
                
            }
            const inside = insidePoints[insidePoints.length - 1];
            const insidePre = insidePoints[insidePoints.length - 2];
            const outsidePre = outsidePoints[outsidePoints.length - 2];
            const outside = outsidePoints[outsidePoints.length - 1];
            index.set([posOffset, posOffset + 1, posOffset + 2, posOffset, posOffset + 2, posOffset + 3], indexOffset);
            position.set([...insidePre, ...outsidePre, ...outside, ...inside], posOffset * 2);

            // 生成顶点相关的信息
            return new Geometry(gl,{
                position:{
                    size:2,
                    data:position
                },
                index:{
                    data:index
                }
            });
        }

        /**
         * OGL用法
         * 1.创建renderer对象
         * 2.创建相机
         * 3.创建场景
         * 4.创建几何体对象
         * 5.创建webgl程序
         * 6.设置网格对象
         * 7.完成渲染
         * */ 

        const canvas = document.querySelector("canvas");
        //  1.创建renderer对象
        const renderer = new Renderer({
            canvas,
            width:512,
            height:512,
        });
        const gl = renderer.gl;
        gl.clearColor(1, 1, 1, 1);

        // 5.创建webgl程序
        const vertex = `
            attribute vec2 position;
            
            void main() {
                gl_PointSize = 10.0;
                float scale = 1.0 / 216.0;
                mat3 scaleM = mat3(
                    scale, 0, 0, 
                    0, -scale, 0,
                    -1, 1, 1
                );
                vec3 pos = scaleM * vec3(position, 1.0);

                gl_Position = vec4(pos.xy, 0.0, 1.0);
            }
        `;
        const fragment = `
            #ifdef GL_ES
                precision mediump float;
            #endif

            void main() {
                gl_FragColor.rgb = vec3(1.2, 0.0, 0.0);
                gl_FragColor.a = 1.0;
            }
        `;
        const program = new Program(gl, {
            vertex,
            fragment
        });
        // 3.创建场景
        const scene = new Transform();
        
        // 4.创建几何体对象
        const points = [ [100, 100], [100, 200], [200, 150], [300, 200], [300, 100]];
        const geometry = extrude(gl, points, { thickness : 20 });
        
        // 6.设置网格对象
        const boxMesh = new Mesh(gl, {geometry, program, mode: gl.TRIANGLE});
        boxMesh.setParent(scene);

        // 7. 完成渲染
        renderer.render({scene});

    </script>
</body>
</html>